/*
 * Copyright (C) 2003, Velocityme Partnership
 * TreeSessionBean.java
 *
 * Created on 13 April 2003, 11:15
 */

package com.velocityme.session;

import com.velocityme.entity.ChangeDeltaItemTypeBean;
import com.velocityme.entity.ChangeDeltaTypeBean;
import com.velocityme.entity.PermissionBean;
import javax.ejb.*;
import javax.naming.*;
import com.velocityme.interfaces.*;
import com.velocityme.utility.InvalidKeyException;
import com.velocityme.utility.PermissionCache;
import com.velocityme.utility.PermissionDeniedException;
import com.velocityme.valueobjects.NodeValue;
import java.rmi.RemoteException;
import java.util.*;

/**
 *
 * @author  Robert Crida Work
 * @ejb.bean
 *           type="Stateless"
 *           cmp-version="2.x"
 *           name="TreeSession"
 *           jndi-name="ejb/TreeSession"
 *           view-type="local"
 *           transaction-type="Container"
 * @ejb.transaction type="Required"
 *
 * @ejb.util generate="physical"
 */
public class TreeSessionBean implements SessionBean {
    
    private SessionContext m_context;
    private SearchSessionLocal m_searchSessionLocal;
    
    private NodeLocal m_directoryNodeLocal;
    
    private PermissionLocal m_permissionNodeEdit;
    private PermissionLocal m_permissionNodeView;
    
    private Map m_nodeTypeOrder;
    
    private Collection getSortedNodeValues(Collection p_nodeLocals) {
        List nodeValues = new ArrayList();
        Iterator it = p_nodeLocals.iterator();
        while (it.hasNext()) {
            NodeLocal nodeLocal = (NodeLocal)it.next();
            nodeValues.add(nodeLocal.getNodeValue());                    
        }
        // sort first into alphabectical order of name
        Collections.sort(nodeValues, new Comparator() {
            public int compare(Object o1, Object o2) {
                NodeValue n1 = (NodeValue)o1;
                NodeValue n2 = (NodeValue)o2;
                return n1.getName().compareTo(n2.getName());
            }
        });
        // sort second into order of type
        Collections.sort(nodeValues, new Comparator() {
            public int compare(Object o1, Object o2) {
                NodeValue n1 = (NodeValue)o1;
                NodeValue n2 = (NodeValue)o2;
                return ((Integer)m_nodeTypeOrder.get(n1.getFinalType())).intValue() -
                       ((Integer)m_nodeTypeOrder.get(n2.getFinalType())).intValue();
            }
        });
        return nodeValues;
    }
    
    /**
     * Gets a list of children nodes that can be seen by this user from the 
     * specified node. If p_node == null then the method returns all nodes 
     * where the user is allocated to roles. This option should be called first 
     * by the client to create the base of the user's tree.
     *
     * @param p_key User's security key
     * @param p_node Current node in which the children will be found
     * @return A collection of nodes that are visible from the specified node
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getVisibleChildrenValue(KeySessionLocal p_keyLocal,
                                                        NodeLocal p_nodeLocal) throws InvalidKeyException {
        try {
            if (p_keyLocal.isValid()) {
                if (p_nodeLocal == null) {
                    Collection rootNodesValue = new TreeSet(new Comparator() {
                        public int compare(Object o1, Object o2) {
                            NodeValue n1 = (NodeValue)o1;
                            NodeValue n2 = (NodeValue)o2;
                            return n1.getNodeId().compareTo(n2.getNodeId());
                        }
                    });
                    // add all nodes from user accesses
                    Iterator i = UserAccessUtil.getLocalHome().findByUser(p_keyLocal.getUserLocal()).iterator();
                    while(i.hasNext()) {
                        UserAccessLocal userAccessLocal = (UserAccessLocal)i.next();
                        rootNodesValue.add(userAccessLocal.getNodeLocal().getNodeValue());
                    }
                    // add all nodes from group accesses
                    i = GroupAccessUtil.getLocalHome().findByUser(p_keyLocal.getUserLocal()).iterator();
                    while(i.hasNext()) {
                        GroupAccessLocal groupAccessLocal = (GroupAccessLocal)i.next();
                        rootNodesValue.add(groupAccessLocal.getNodeLocal().getNodeValue());
                    }
                    // add all sprints for this person
                    i = SprintUtil.getLocalHome().findByPersonDate(p_keyLocal.getUserLocal().getPersonLocal(), new Date()).iterator();
                    while(i.hasNext()) {
                        SprintLocal sprintLocal = (SprintLocal)i.next();
                        rootNodesValue.add(sprintLocal.getNodeLocal().getNodeValue());
                    }
                    // now convert to list sorted by id and insert user and person at end
                    rootNodesValue = new ArrayList(rootNodesValue);
                    rootNodesValue.add(p_keyLocal.getUserLocal().getNodeLocal().getNodeValue());
                    rootNodesValue.add(p_keyLocal.getUserLocal().getPersonLocal().getContactableLocal().getNodeLocal().getNodeValue());
                    rootNodesValue.add(m_directoryNodeLocal.getNodeValue());
                    return rootNodesValue;
                }
                else {
                    Collection childrenLocal = p_nodeLocal.ejbSelectOrderedChildren(p_nodeLocal);
                    return getSortedNodeValues(childrenLocal);
                }
            }
            else
                throw new InvalidKeyException();
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
        catch(FinderException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * Gets a list of ancestor nodes that can be seen by this user from the 
     * specified node. If p_node == null then the method returns an empty collection.
     *
     * @param p_key User's security key
     * @param p_node Current node in which the children will be found
     * @return A collection of nodes that are visible from the specified node
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getVisibleAncestorValue(KeySessionLocal p_keyLocal,
                                                        NodeLocal p_nodeLocal) throws InvalidKeyException {
        if (p_keyLocal.isValid()) {
            if (p_nodeLocal == null) {
                return new ArrayList();
            }
            else {
                List visibleAncestorValues = new ArrayList();
                do {
                    if (!m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_nodeLocal).contains(m_permissionNodeView))
                        break;
                    visibleAncestorValues.add(0, p_nodeLocal.getNodeValue());
                } while((p_nodeLocal = p_nodeLocal.getParentNodeLocal()) != null);
                return visibleAncestorValues;
            }
        }
        else
            throw new InvalidKeyException();
    }
    
    /**
     * Gets a list of sibling nodes that can be seen by this user from the 
     * specified node. If p_node == null then the method returns null.
     *
     * @param p_key User's security key
     * @param p_node Current node for which the siblings will be found
     * @return A collection of sibling nodes that are visible from the specified node
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getVisibleSiblingsValue(KeySessionLocal p_keyLocal,
                                                        NodeLocal p_nodeLocal) throws InvalidKeyException {
        if (p_keyLocal.isValid()) {
            if (p_nodeLocal == null)
                return null;
            NodeLocal parentNodeLocal = p_nodeLocal.getParentNodeLocal();
            if (parentNodeLocal == null)
                return null;
            if (!m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, parentNodeLocal).contains(m_permissionNodeView))
                return null;
            NodeValue nodeValue = p_nodeLocal.getNodeValue();
            Collection visibleSiblingsValue = getVisibleChildrenValue(p_keyLocal, parentNodeLocal);
            visibleSiblingsValue.remove(nodeValue);
            return visibleSiblingsValue;
        }
        else
            throw new InvalidKeyException();
    }
    
    /**
     * Gets a list of link nodes that can be seen by this user from the 
     * specified node.
     *
     * @param p_key User's security key
     * @param p_node Current node in which the links will be found
     * @return A collection of nodes that are visible from the specified node
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getVisibleLinkedValue(KeySessionLocal p_keyLocal,
                                                      NodeLocal p_nodeLocal) throws InvalidKeyException {
        if (p_keyLocal.isValid()) {
            if (p_nodeLocal == null)
                return null;
            else {
                Collection linksLocal = p_nodeLocal.getAllLinkedNodesLocal();
                return getSortedNodeValues(linksLocal);
            }
        }
        else
            throw new InvalidKeyException();
    }
    
    /**
     * Gets a list of dependency tasks that can be seen by this user from the 
     * specified task.
     *
     * @param p_key User's security key
     * @param p_task Current task in which the dependencies will be found
     * @return A collection of tasks that are visible from the specified task
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getVisibleDependencyTaskValues(KeySessionLocal p_keyLocal,
                                                               TaskLocal p_taskLocal) throws InvalidKeyException {
        if (p_keyLocal.isValid()) {
            if (p_taskLocal == null)
                return null;
            else {
                Collection linksLocal = new ArrayList();
                Iterator i = p_taskLocal.getDependencyLinksLocal().iterator();
                while(i.hasNext()) {
                    TaskDependencyLocal taskDependencyLocal = (TaskDependencyLocal)i.next();
                    linksLocal.add(taskDependencyLocal.getDependencyTaskLocal().getNodeLocal());
                }
                return getSortedNodeValues(linksLocal);
            }
        }
        else
            throw new InvalidKeyException();
    }
    
    /**
     * Gets a list of dependant tasks that can be seen by this user from the 
     * specified task.
     *
     * @param p_key User's security key
     * @param p_task Current task in which the dependants will be found
     * @return A collection of tasks that are visible from the specified task
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getVisibleDependantTaskValues(KeySessionLocal p_keyLocal,
                                                              TaskLocal p_taskLocal) throws InvalidKeyException {
        if (p_keyLocal.isValid()) {
            if (p_taskLocal == null)
                return null;
            else {
                Collection linksLocal = new ArrayList();
                Iterator i = p_taskLocal.getDependantLinksLocal().iterator();
                while(i.hasNext()) {
                    TaskDependencyLocal taskDependencyLocal = (TaskDependencyLocal)i.next();
                    linksLocal.add(taskDependencyLocal.getDependantTaskLocal().getNodeLocal());
                }
                return getSortedNodeValues(linksLocal);
            }
        }
        else
            throw new InvalidKeyException();
    }
    
    /**
     * Gets a list of bookmarked nodes for the current user.
     *
     * @param p_key User's security key
     * @return A collection of bookmarked nodes for the current user
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getBookmarkNodeValues(KeySessionLocal p_keyLocal) throws InvalidKeyException {
        if (p_keyLocal.isValid())
            return getSortedNodeValues(p_keyLocal.getUserLocal().getBookmarkNodesLocal());
        else
            throw new InvalidKeyException();
    }
    
    /**
     * Gets a list of node links that can be seen by this user from the 
     * specified node.
     *
     * @param p_key User's security key
     * @param p_node Current node in which the links will be found
     * @return A collection of node links that are visible from the specified node
     *
     * @ejb.interface-method view-type="local"
     **/
    public java.util.Collection getNodeLinkValues(KeySessionLocal p_keyLocal,
                                                  NodeLocal p_nodeLocal) throws InvalidKeyException {
        try {                                                      
            if (p_keyLocal.isValid()) {
                Collection nodeLinkValues = new ArrayList();
                Iterator i = NodeLinkUtil.getLocalHome().findByNode(p_nodeLocal).iterator();
                while(i.hasNext()) {
                    NodeLinkLocal nodeLinkLocal = (NodeLinkLocal)i.next();
                    nodeLinkValues.add(nodeLinkLocal.getNodeLinkValue());
                }
                return nodeLinkValues;
            }
            else
                throw new InvalidKeyException();
        } catch(NamingException e) {
            throw new EJBException(e);
        } catch(FinderException e) {
            throw new EJBException(e);
        }
    }
    
     /**
     * Checks to see if it is a legal paste node
     *
     * @param p_key User's security key
     * @param p_target The target node to which the node will be moved
     * @param p_child The node that is to be moved
     *
     * @ejb.interface-method view-type="local"
     **/
    public boolean isValidPasteNode(KeySessionLocal p_keyLocal, NodeLocal p_targetLocal, NodeLocal p_childLocal)
        throws InvalidKeyException {
        // can't move system nodes
        if (!p_childLocal.getFinalType().equals("SystemNode") &&
            m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_childLocal).contains(m_permissionNodeEdit) &&
            m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_targetLocal).contains(m_permissionNodeEdit)) {
            NodeLocal parentLocal = p_targetLocal;
            int childNodeId = p_childLocal.getNodeId().intValue();
            // false if the destination is its current location
            if (p_targetLocal.getNodeId().intValue() == p_childLocal.getParentNodeLocal().getNodeId().intValue())
                return false;
            // now make sure that the target node is not a child of the node being moved
            while(parentLocal != null) {
                if (parentLocal.getNodeId().intValue() == childNodeId)
                    return false;
                parentLocal = parentLocal.getParentNodeLocal();
            }
            return true;
        }
        return false;
    }
    
     /**
     * Checks to see if it is a legal link node
     *
     * @param p_key User's security key
     * @param p_target The target node to which the node will be linked
     * @param p_marked The node that is to be linked
     *
     * @ejb.interface-method view-type="local"
     **/
    public boolean isValidLinkNode(KeySessionLocal p_keyLocal, NodeLocal p_targetLocal, NodeLocal p_markedLocal)
        throws InvalidKeyException {
        // can't link system nodes
        if (p_markedLocal.getFinalType().equals("SystemNode") ||
            p_targetLocal.getFinalType().equals("SystemNode"))
            return false;
        // can't link if link already exists
        try {
            if (NodeLinkUtil.getLocalHome().findByNodes(p_markedLocal, p_targetLocal).size() > 0)
                return false;
        } catch(NamingException e) {
            throw new EJBException(e);
        } catch(FinderException e) {
            // doesn't exist so continue!
        }
        if (!p_markedLocal.equals(p_targetLocal) &&
            m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_markedLocal).contains(m_permissionNodeEdit) &&
            m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_targetLocal).contains(m_permissionNodeEdit)) {
            return true;
        }
        return false;
    }
    
    /**
     * Moves the node in the tree to the target node
     *
     * @param p_key User's security key
     * @param p_target The target node to which the node will be moved
     * @param p_child The node that is to be moved
     *
     * @ejb.interface-method view-type="local"
     **/
    public void moveChildNode(KeySessionLocal p_keyLocal, NodeLocal p_targetLocal, NodeLocal p_childLocal)
        throws InvalidKeyException, PermissionDeniedException {
        try {
            if (isValidPasteNode(p_keyLocal, p_targetLocal, p_childLocal)) {
                NodeLocal parentLocal = p_childLocal.getParentNodeLocal();
                ChangeDeltaLocal changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.MOVE, p_keyLocal.getUserLocal(), p_childLocal.getDBObjectLocal());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.PARENT_NODE, p_targetLocal.getPathName());
                p_childLocal.setParentNodeLocal(p_targetLocal);
                // invalidate the permission cache
                PermissionCache.getInstance().clear();
                // now record change in the original parent
                changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.MOVE_CHILD, p_keyLocal.getUserLocal(), parentLocal.getDBObjectLocal());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.CHILD_NODE, p_childLocal.getName());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.DESTINATION_NODE, p_targetLocal.getPathName());
            }
            else
                throw new PermissionDeniedException();
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * Links the node in the tree to the target node
     *
     * @param p_key User's security key
     * @param p_target The target node to which the node will be moved
     * @param p_marked The node that is to be linked
     *
     * @ejb.interface-method view-type="local"
     **/
    public void createNodeLink(KeySessionLocal p_keyLocal, NodeLocal p_targetLocal, NodeLocal p_markedLocal)
        throws InvalidKeyException, PermissionDeniedException {
        try {
            if (isValidLinkNode(p_keyLocal, p_targetLocal, p_markedLocal)) {
                NodeLinkUtil.getLocalHome().create(p_targetLocal, p_markedLocal);
                ChangeDeltaLocal changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.CREATE_LINK, p_keyLocal.getUserLocal(), p_markedLocal.getDBObjectLocal());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.NODE, p_targetLocal.getPathName());
                // now record change in the original parent
                changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.CREATE_LINK, p_keyLocal.getUserLocal(), p_targetLocal.getDBObjectLocal());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.NODE, p_markedLocal.getPathName());
            }
            else
                throw new PermissionDeniedException();
        }
        catch(CreateException e) {
            throw new EJBException(e);
        }
        catch(NamingException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * Remove a link between a pair of nodes
     *
     * @param p_key User's security key
     * @param p_nodeLink The node link
     *
     * @ejb.interface-method view-type="local"
     **/
    public void deleteNodeLink(KeySessionLocal p_keyLocal, NodeLinkLocal p_nodeLinkLocal)
        throws InvalidKeyException, PermissionDeniedException {
        try {
            if (m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_nodeLinkLocal.getFromNodeLocal()).contains(m_permissionNodeEdit) &&
                m_searchSessionLocal.getAssignedPermissionsLocal(p_keyLocal, p_nodeLinkLocal.getToNodeLocal()).contains(m_permissionNodeEdit)) {
		// record the delete at both ends of the link
                ChangeDeltaLocal changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.DELETE_LINK, p_keyLocal.getUserLocal(), p_nodeLinkLocal.getFromNodeLocal().getDBObjectLocal());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.NODE, p_nodeLinkLocal.getToNodeLocal().getPathName());
                changeDeltaLocal = ChangeDeltaUtil.getLocalHome().create(new Date(), ChangeDeltaTypeBean.DELETE_LINK, p_keyLocal.getUserLocal(), p_nodeLinkLocal.getToNodeLocal().getDBObjectLocal());
                changeDeltaLocal.addItem(ChangeDeltaItemTypeBean.NODE, p_nodeLinkLocal.getFromNodeLocal().getPathName());
                // now delete the link
                p_nodeLinkLocal.remove();
            }
            else
                throw new PermissionDeniedException();
        } catch(CreateException e) {
            throw new EJBException(e);
        } catch(NamingException e) {
            throw new EJBException(e);
        } catch(RemoveException e) {
            throw new EJBException(e);
        }
    }
    
    /**
     * Create the Session Bean.
     * @throws CreateException 
     * @ejb:create-method view-type="remote"
     */
    public void ejbCreate() throws CreateException
    {
        try {
            m_searchSessionLocal = SearchSessionUtil.getLocalHome().create();
            m_directoryNodeLocal = NodeUtil.getLocalHome().findDirectoryNode();
            m_permissionNodeEdit = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.NODE_EDIT)));
            m_permissionNodeView = PermissionUtil.getLocalHome().findByPrimaryKey(new PermissionPK(new Integer(PermissionBean.NODE_VIEW)));
        }
        catch(NamingException e) {
            throw new CreateException(e.getMessage());
        }
        catch(FinderException e) {
            throw new CreateException(e.getMessage());
        }
        m_nodeTypeOrder = new HashMap();
        m_nodeTypeOrder.put("SystemNode", new Integer(0));
        m_nodeTypeOrder.put("SystemProperties", new Integer(1));
        m_nodeTypeOrder.put("ContactDetailTypes", new Integer(2));
        m_nodeTypeOrder.put("LicenceManagement", new Integer(3));
        m_nodeTypeOrder.put("LoginHistory", new Integer(4));
        m_nodeTypeOrder.put("Directory", new Integer(5));
        m_nodeTypeOrder.put("Node", new Integer(6));
        m_nodeTypeOrder.put("Organisation", new Integer(7));
        m_nodeTypeOrder.put("Department", new Integer(8));
        m_nodeTypeOrder.put("Person", new Integer(9));
        m_nodeTypeOrder.put("Group", new Integer(10));
        m_nodeTypeOrder.put("User", new Integer(11));
        m_nodeTypeOrder.put("State", new Integer(12));
        m_nodeTypeOrder.put("Status", new Integer(13));
        m_nodeTypeOrder.put("TaskStateMachine", new Integer(14));
        m_nodeTypeOrder.put("Role", new Integer(15));
        m_nodeTypeOrder.put("Sprint", new Integer(16));
        m_nodeTypeOrder.put("Project", new Integer(17));
        m_nodeTypeOrder.put("Anchor", new Integer(18));
        m_nodeTypeOrder.put("Action", new Integer(19));
        m_nodeTypeOrder.put("Issue", new Integer(20));
        m_nodeTypeOrder.put("Note", new Integer(21));
    }

    public void ejbActivate() throws java.rmi.RemoteException {
    }    
    
    public void ejbPassivate() throws java.rmi.RemoteException {
    }
    
    public void ejbRemove() throws java.rmi.RemoteException {
    }
    
    public void setSessionContext(javax.ejb.SessionContext sessionContext) throws java.rmi.RemoteException {
        m_context = sessionContext;
    }
    
}
